package ve.usb.coord.colision;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;

import ve.usb.cohesion.runtime.CohesionAction;

import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
import ve.usb.coord.colision.hibernate.*;


/**
 * Manejo de elementos de la entidad Horario.<br>
 */
public class AccionesHorario extends CohesionAction {
    
/**
     * Acción llamada por Struts para ejecutar la acción HorarioOfertaCrearI.
     * Prepara la vista de captura de datos de una entidad para realizar una inserción.<br>
     * @param mapping The ActionMapping used to select this instance.
     * @param form The optional ActionForm bean for this request.
     * @param request The HTTP Request we are processing.
     * @param response The HTTP Response we are processing.
     * @return The Struts name of the following step.
     * @throws java.lang.Exception For untreated exceptions. 
     * These exceptios will normally be treated with 
     * the default exception action.
     */
    public ActionForward HorarioOfertaCrearI(ActionMapping mapping, ActionForm  form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        
        //Salidas
        final String SALIDA_0 = "horarioOfertaCrear";

        String salida = SALIDA_0;        
        Session s = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction tr = s.beginTransaction();
        try {

            HttpSession sess = request.getSession();
            FHorario fFHorario = (FHorario)sess.getAttribute("FHorario");
            if (fFHorario==null) {
                fFHorario = new FHorario();
                sess.setAttribute("FHorario", fFHorario);
            } else {
                fFHorario.reset(mapping, request);
            }
             
            //Feed for combo listOferta.
            {java.util.List<ve.usb.cohesion.runtime.Pair> list = 
               (java.util.List<ve.usb.cohesion.runtime.Pair>)s.createQuery(
               "select new ve.usb.cohesion.runtime.Pair(periodo.nombre, idOferta) " + 
               "from Oferta").list();
             list.add(0,new ve.usb.cohesion.runtime.Pair("Ninguna", 0));
             fFHorario.setListOferta(list);
            }

            fFHorario.setOferta(Long.parseLong((String)request.getParameter("idOferta")));

            tr.commit();

        } catch (Exception ex) {
            tr.rollback();
            throw ex;
        }
        return mapping.findForward(salida);
    }

/**
     * Acción llamada por Struts para ejecutar la acción HorarioI.
     * Prepara la vista de actualización de una entidad.<br>
     * @param mapping The ActionMapping used to select this instance.
     * @param form The optional ActionForm bean for this request.
     * @param request The HTTP Request we are processing.
     * @param response The HTTP Response we are processing.
     * @return The Struts name of the following step.
     * @throws java.lang.Exception For untreated exceptions. 
     * These exceptios will normally be treated with 
     * the default exception action.
     */
    public ActionForward HorarioI(ActionMapping mapping, ActionForm  form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        
        //Salidas
        final String SALIDA_0 = "horario";

        String salida = SALIDA_0;        
        Session s = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction tr = s.beginTransaction();
        try {

            String id = request.getParameter("idHorario");
            Long idHorario = (id==null) ? 
                (Long)request.getSession().getAttribute("idHorario") :
                new Long(id);
            
            HttpSession sess = request.getSession();
            FHorario fFHorario = (FHorario)sess.getAttribute("FHorario");
            if (fFHorario==null) {
                fFHorario = new FHorario();
                sess.setAttribute("FHorario", fFHorario);
            } else {
                fFHorario.reset(mapping, request);
            }
            Horario horario = (Horario)s.load(Horario.class, idHorario);
            fFHorario.setIdHorario(horario.getIdHorario());

            fFHorario.setOferta(horario.getOferta().getIdOferta());

            fFHorario.setDia(horario.getDia());
            fFHorario.setHora(horario.getHora());

            //Feed for combo listOferta.
            {java.util.List<ve.usb.cohesion.runtime.Pair> list = 
               (java.util.List<ve.usb.cohesion.runtime.Pair>)s.createQuery(
               "select new ve.usb.cohesion.runtime.Pair(periodo.nombre, idOferta) " + 
               "from Oferta").list();
             list.add(0,new ve.usb.cohesion.runtime.Pair("Ninguna", 0));
             fFHorario.setListOferta(list);
            }


            request.getSession().setAttribute("idOferta", fFHorario.getOferta());

            request.getSession().setAttribute("idHorario", idHorario);

            tr.commit();

        } catch (Exception ex) {
            tr.rollback();
            throw ex;
        }
        return mapping.findForward(salida);
    }

/**
     * Acción llamada por Struts para ejecutar la acción HorarioAct.
     * Registro de actualizaciones de una entidad.<br>
     * @param mapping The ActionMapping used to select this instance.
     * @param form The optional ActionForm bean for this request.
     * @param request The HTTP Request we are processing.
     * @param response The HTTP Response we are processing.
     * @return The Struts name of the following step.
     * @throws java.lang.Exception For untreated exceptions. 
     * These exceptios will normally be treated with 
     * the default exception action.
     */
    public ActionForward HorarioAct(ActionMapping mapping, ActionForm  form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        
        //Salidas
        final String SALIDA_0 = "horario";

        String salida = SALIDA_0;        
        Session s = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction tr = s.beginTransaction();
        try {
            FHorario fFHorario = (FHorario)form;

            Horario horario = (Horario)s.load(Horario.class, new Long(fFHorario.getIdHorario()));
            horario.setDia(fFHorario.getDia());
            horario.setHora(fFHorario.getHora());
            horario.setTipoHora(fFHorario.getTipoHora());

            //Feed for combo listOferta.
            {java.util.List<ve.usb.cohesion.runtime.Pair> list = 
               (java.util.List<ve.usb.cohesion.runtime.Pair>)s.createQuery(
               "select new ve.usb.cohesion.runtime.Pair(periodo.nombre, idOferta) " + 
               "from Oferta").list();
             list.add(0,new ve.usb.cohesion.runtime.Pair("Ninguna", 0));
             fFHorario.setListOferta(list);
            }
            
            
            String crit = " where oferta1 = '" + horario.getOferta().getIdOferta();
            crit += "' or oferta2 = '" + horario.getOferta().getIdOferta()+"'";
            java.util.List<Colision> listaColision = (java.util.List<Colision>)s.createQuery("from Colision"+crit).list();
            java.util.Iterator<Colision> iterColision = listaColision.iterator();
            while( iterColision.hasNext() ){
                s.delete(iterColision.next());
            }
            
            detectarColision(s,horario.getOferta(),horario);
            
            request.getSession().setAttribute("idOferta", fFHorario.getOferta());

            request.getSession().setAttribute("idHorario", horario.getIdHorario());

            tr.commit();

        } catch (Exception ex) {
            tr.rollback();
            throw ex;
        }
        return mapping.findForward(salida);
    }

/**
     * Acción llamada por Struts para ejecutar la acción HorarioElim.
     * Elimina la entidad seleccionada.<br>
     * @param mapping The ActionMapping used to select this instance.
     * @param form The optional ActionForm bean for this request.
     * @param request The HTTP Request we are processing.
     * @param response The HTTP Response we are processing.
     * @return The Struts name of the following step.
     * @throws java.lang.Exception For untreated exceptions. 
     * These exceptios will normally be treated with 
     * the default exception action.
     */
    public ActionForward HorarioElim(ActionMapping mapping, ActionForm  form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        
        //Salidas
        final String SALIDA_0 = "OfertaI";
        final String SALIDA_1 = "horario";

        String salida = SALIDA_0;        
        Session s = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction tr = s.beginTransaction();
        try {

            String id = request.getParameter("idHorario");
            Long idHorario = (id==null) ? 
                (Long)request.getSession().getAttribute("idHorario") :
                new Long(id);
            Horario horario = (Horario)s.load(Horario.class, idHorario);

            long idOferta = horario.getOferta().getIdOferta();
            horario.getOferta().removeFromHorario(horario);

            try {
                s.flush();
                request.getSession().setAttribute("idHorario", idHorario);

                request.getSession().setAttribute("idOferta", idOferta);

            } catch (Exception ex1) {
                ex1.printStackTrace();
                salida = SALIDA_1;
                request.setAttribute("msg", "No se pudo eliminar entidad.");
            }

            tr.commit();

        } catch (Exception ex) {
            tr.rollback();
            throw ex;
        }
        return mapping.findForward(salida);
    }

/**
     * Acción llamada por Struts para ejecutar la acción HorarioCrear.
     * Registro de la creación de una horario.<br>
     * @param mapping The ActionMapping used to select this instance.
     * @param form The optional ActionForm bean for this request.
     * @param request The HTTP Request we are processing.
     * @param response The HTTP Response we are processing.
     * @return The Struts name of the following step.
     * @throws java.lang.Exception For untreated exceptions. 
     * These exceptios will normally be treated with 
     * the default exception action.
     */
    public ActionForward HorarioCrear(ActionMapping mapping, ActionForm  form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        
        //Salidas
        final String SALIDA_1 = "horario";

        String salida = SALIDA_1;
        Session s = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction tr = s.beginTransaction();
        try {
            FHorario fFHorario = (FHorario)form;

            Horario horario = new Horario();
            horario.setDia(fFHorario.getDia());
            horario.setHora(fFHorario.getHora());
            horario.setTipoHora(fFHorario.getTipoHora());
            Oferta obj = (Oferta)s.load(Oferta.class, new Long(fFHorario.getOferta()));
            obj.addToHorario(horario);

            s.flush();
            fFHorario.setIdHorario(horario.getIdHorario());
            //Feed for combo listOferta.
            {java.util.List<ve.usb.cohesion.runtime.Pair> list = 
               (java.util.List<ve.usb.cohesion.runtime.Pair>)s.createQuery(
               "select new ve.usb.cohesion.runtime.Pair(periodo.nombre, idOferta) " + 
               "from Oferta").list();
             list.add(0,new ve.usb.cohesion.runtime.Pair("Ninguna", 0));
             fFHorario.setListOferta(list);
            }

            //fFHorario.setOferta(horario.getOferta().getIdOferta());
            request.getSession().setAttribute("idOferta", fFHorario.getOferta());

            request.getSession().setAttribute("idHorario", horario.getIdHorario());
            
            detectarColision(s,obj,horario);
            
            tr.commit();

        } catch (Exception ex) {
            tr.rollback();
            throw ex;
        }
        return mapping.findForward(salida);
    }
    
    //Deteccion de colisiones 
            
    private void detectarColision(Session s, Oferta obj, Horario horario){
            
            //Se obtienen todas las Modalidades del Plan de Estudio
            String crit = " where plandeestudios = " + obj.getPlanDeEstudios().getIdPlan();
            java.util.List<Modalidad> listaModalidad = (java.util.List<Modalidad>)s.createQuery("from Modalidad"+crit).list();
            java.util.Iterator IterModalidad = listaModalidad.iterator();

            
            crit =  " where idOferta != "+obj.getIdOferta();
            crit += " and plandeestudios = "+obj.getPlanDeEstudios().getIdPlan();
            crit += " and periodo = "+obj.getPeriodo().getIdPeriodo();
            java.util.List<Oferta> listaOferta = (java.util.List<Oferta>)s.createQuery("from Oferta"+crit).list();
            java.util.Iterator IterOferta;
            
            Oferta ofertaT = null;
            Horario horarioT = null;
            Colision colisionT = null;
            Modalidad modalidadT = null;
            Asociacion asociacionTA = null;
            Asociacion asociacionTB = null;
            
            /* Acá se verifican las restricciones de tiempo.
             *
             * NOTA
             * Una vez descartadas las ofertas no se tomaran en cuenta para otras modalidades
             * De esta manera se supone que la distancia entre las ofertas para las distintas
             * modalidades se mantendran gracias a los requisitos
             */
            while(IterModalidad.hasNext()){
                modalidadT = (Modalidad)IterModalidad.next();
                crit =  " where modalidad = "+modalidadT.getIdModalidad();
                crit += " and requisito = "+obj.getRequisito().getIdRequisito();
                
                if(!s.createQuery("from Asociacion"+crit).list().isEmpty()){
                    asociacionTA =(Asociacion)s.createQuery("from Asociacion"+crit).list().get(0);
                    IterOferta = listaOferta.iterator();
                    while(IterOferta.hasNext()){
                        ofertaT = (Oferta)IterOferta.next();
                        crit =  " where modalidad = "+modalidadT.getIdModalidad();
                        crit += " and requisito = "+ofertaT.getRequisito().getIdRequisito();
                        if(!s.createQuery("from Asociacion"+crit).list().isEmpty()){
                            asociacionTB = (Asociacion)s.createQuery("from Asociacion"+crit).list().get(0);
                            //Se eliminan las ofertas de aquellas asignaturas que esten distanciadas por mas de 3 periodos
                            if(obtenerDiferenciaPeriodoAbs(asociacionTA,asociacionTB) > 3){
                                IterOferta.remove();
                            }
                        }
                    }                
                }
            }
            
            /* Acá se verifican las restricciones de requisitos.
             */
            IterOferta = listaOferta.iterator();            
            while(IterOferta.hasNext()){
                ofertaT = (Oferta)IterOferta.next();
                if(verificarCorrequisitos(obj,ofertaT,s))
                    IterOferta.remove();
                
            }
            
            IterOferta = listaOferta.iterator();
            while(IterOferta.hasNext()){
                ofertaT = (Oferta) IterOferta.next();
                java.util.Iterator IterHorario = ofertaT.getHorario().iterator();
                while(IterHorario.hasNext()){
                    horarioT  = (Horario) IterHorario.next();
                    if(horario.getDia() == horarioT.getDia()){
                        if(horario.getHora() == horarioT.getHora()){
                                colisionT = crearColision(obj,ofertaT);
                                if(colisionT != null) s.save(colisionT);
                        }
                    }
                }
            }
            
    }
    
    private int obtenerDiferenciaPeriodoAbs(Asociacion a,Asociacion b){
        int ia = Integer.parseInt(""+a.getPeriodoAbs().substring(1));
        int ib = Integer.parseInt(""+b.getPeriodoAbs().substring(1));
        return java.lang.Math.abs(ia - ib);
    }
    
    private boolean verificarCorrequisitos(Oferta oferA, Oferta oferB, Session s){
        java.util.HashSet<String> reqsA = new java.util.HashSet();
        java.util.HashSet<String> reqsB = new java.util.HashSet();
        
        reqsA = cadenaRequisitos(oferA.getRequisito().getAsignatura().getCodigo(), oferA.getRequisito().getGrupo().getIdGrupo(), s);
        reqsB = cadenaRequisitos(oferB.getRequisito().getAsignatura().getCodigo(), oferB.getRequisito().getGrupo().getIdGrupo(), s);
        
        if(reqsA.contains(oferB.getRequisito().getAsignatura().getCodigo())){
            return true;
        } else if (reqsB.contains(oferA.getRequisito().getAsignatura().getCodigo())){
            return true;
        } else {
            return false;
        }
    }
    
    private java.util.HashSet<String> cadenaRequisitos(String asig, long grp, Session s){
            String crit =  " where codigo = '"+asig+"'";
            java.util.List L_Asignatura = (java.util.List<Asignatura>)s.createQuery("from Asignatura"+crit).list();
            if(L_Asignatura.isEmpty())
                return new java.util.HashSet();
            Asignatura  Asig = (Asignatura)L_Asignatura.get(0);
            
            crit = " where asignatura = '"+Asig.getIdAsignatura()+"'";
            crit += "and grupo = "+grp;
            java.util.List L_Requisitos = (java.util.List<Requisitos>)s.createQuery("from Requisitos"+crit).list();
            if(L_Requisitos.isEmpty())
                return new java.util.HashSet();
            Requisitos Req =  (Requisitos) L_Requisitos.get(0);
            
            String[] reqTemp = Req.getRequisitos().split(";");
            reqTemp = reqTemp[0].split(",");
            
            java.util.HashSet<String> cadena = new java.util.HashSet();
            if(!reqTemp[0].equals("false")){
                for(int i = 0; i < reqTemp.length ; i++){
                    cadena.add(reqTemp[i]);
                    System.out.println("Agregando el codigo a la lista :"+reqTemp[i]);
                }
            }
            java.util.Iterator cadenaIter = cadena.iterator();
            while(cadenaIter.hasNext()){
                cadena.addAll(cadenaRequisitos(((String)cadenaIter.next()), grp, s));
            }
            return cadena ;
    }
    
    private Colision crearColision(Oferta oferA, Oferta oferB){
        Colision colision = new Colision();
        colision.setOferta1(oferA);
        colision.setOferta2(oferB);
        colision.setCausa("Causa Desconocida");
        Integer gA = new Integer(oferA.getRequisito().getGrupo().getPrioridad());
        Integer gB = new Integer(oferB.getRequisito().getGrupo().getPrioridad());
        colision.setGravedad(Byte.parseByte(""+(gA.intValue() + gB.intValue())));
        return colision;
    }



}